Skip to content

feat(types): declare bucket_id, size and filename on the upload and image domains - #116

Merged
pyramation merged 1 commit into
mainfrom
feat/upload-bucket-id
Aug 7, 2026
Merged

feat(types): declare bucket_id, size and filename on the upload and image domains#116
pyramation merged 1 commit into
mainfrom
feat/upload-bucket-id

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

public.upload / public.image documents are projections of a files row ({id, key, mime, bucket_id, size, filename, versions}, constructive-planning#1476), but the domain contract only constrained bucket — the bucket name — and said nothing about bucket_id, the uuid the platform actually resolves storage with. A document could carry a garbage bucket_id and validate fine.

This widens both checks; nothing becomes required, so url-only external references and every existing document keep validating:

+  AND (NOT value ? 'bucket_id' OR (value->>'bucket_id') ~ '^[0-9a-fA-F]{8}-…-[0-9a-fA-F]{12}$')
+  AND (NOT value ? 'size'      OR jsonb_typeof(value->'size') = 'number')
+  AND (NOT value ? 'filename'  OR jsonb_typeof(value->'filename') = 'string')

size and filename: yes, they belong. #1476 lists both in the authoritative files shape and in the projection the upload path is supposed to write (#1477 shows today's client dropping fileId/key and persisting a presigned url instead). Constraining them costs nothing, is a widening, and stops a size: "12345" string from silently entering the projection.

Why a regex and not ::uuid. A domain CHECK must be immutable and cannot trap an error, so a cast would abort the statement instead of rejecting the row, and jsonb_typeof(...) = 'string' alone accepts "not-a-uuid". The regex demands the canonical 8-4-4-4-12 hex form — deliberately stricter than PostgreSQL's uuid input (which also accepts braces/unhyphenated), because every producer here writes uuid::text. Non-string json fails too: ->> renders 12345 as '12345', which the pattern rejects.

Why a new change rather than editing domains/upload. pgpm is deterministic — already-deployed databases never re-run a change, so editing the original CREATE DOMAIN would silently leave them on the old contract. Instead schemas/public/domains/{upload,image}_file_ref_keys sits on top of it and rewrites the constraint by name:

ALTER DOMAIN upload DROP CONSTRAINT upload_check;
ALTER DOMAIN upload ADD  CONSTRAINT upload_check CHECK ( … );

upload_check is exactly the name PostgreSQL gave the anonymous CHECK in the original change, so assert_domain('public.upload'::regtype, 'jsonb'::regtype, _constraints => 1) still holds and the revert restores the previous predicate verbatim. Verify does not just look at the catalog: it casts a full projection, a non-uuid bucket_id, a string size and a url-only document, and fails if the contract disagrees.

Testing

  • packages/types: 39 tests pass (describe.each(['upload','image']) covers full projection accepted, non-uuid / unhyphenated / non-string bucket_id rejected, size/filename typed, url-only still accepted, all keys optional).
  • pgpm test-packages --full-cycle across the workspace: all modules deploy → verify → revert clean.
  • sql/pgpm-types--0.39.0.{sql,bundle.tar.gz} regenerated with pgpm package.

Based on #115 (the sync PR), which this needs for the 0.39.0 line and the assert_* verify style.

Link to Devin session: https://app.devin.ai/sessions/43b0b10711174fa49d51907bf2b0c25f
Requested by: @pyramation

…mage domains

The documents are projections of a files row, so the contract now constrains
the keys the platform actually resolves storage with. bucket_id must parse as
a canonical uuid; size and filename get json type checks. Every key stays
optional, so url-only external references keep validating.
@pyramation pyramation self-assigned this Aug 7, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Base automatically changed from feat/sync-constructive-db-catalog-assertions to main August 7, 2026 23:31
@pyramation
pyramation merged commit 6943ebc into main Aug 7, 2026
25 checks passed
@pyramation
pyramation deleted the feat/upload-bucket-id branch August 7, 2026 23:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant